﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using YAPSAR.Domain;
using NHibernate;
using NHibernate.Tool.hbm2ddl;
using FluentNHibernate.Automapping;
using NHibernate.Context;

namespace YAPSAR.Database
{
    class DatabaseManager
    {
        private static DatabaseManager static_instance;
        public static DatabaseManager Instance
        {
            get
            {
                if (static_instance == null)
                {
                    static_instance = new DatabaseManager();
                }

                return static_instance;
            }
        }

        private ISessionFactory SessionFactory { get; set; }
        public ISession Session
        {
            get
            {
                if (CurrentSessionContext.HasBind(this.SessionFactory) == false)
                {
                    // There is no current session, so we will bind one from the current session factory.
                    CurrentSessionContext.Bind(this.SessionFactory.OpenSession());
                }

                return this.SessionFactory.GetCurrentSession();
            }
        }

        private DatabaseManager()
        {
        }

        private class AutoMappingConfiguration : DefaultAutomappingConfiguration
        {
            public override bool ShouldMap(Type type)
            {
                return type.Namespace == "YAPSAR.Domain";
            }
        }

        public void Start()
        {
            if (this.SessionFactory != null)
            {
                throw new InvalidOperationException("Cannot run Start() on DatabaseInitializer more than once.");
            }

            var mappingConfiguration = new AutoMappingConfiguration();
            FluentConfiguration fluentConfiguration = Fluently.Configure()
                .Database(SQLiteConfiguration.Standard.UsingFile("storage.db"))
                .CurrentSessionContext("thread_static")

                .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Project>(mappingConfiguration)

                    .Override<WorkUnit>(map => { map.IgnoreProperty(x => x.FrequencyMouse); })
                    .Override<WorkUnit>(map => { map.References<ProcessInstance>(x => x.ProcessInstance).Cascade.SaveUpdate(); })

                    .Override<Process>(map => { map.HasMany<Tag>(x => x.Tags).Cascade.SaveUpdate(); })
                    .Override<Process>(map => { map.HasMany<ProcessInstance>(x => x.Instances).Cascade.SaveUpdate(); })

                    .Override<Project>(map => { map.HasMany<ProjectTag>(x => x.Tags).Cascade.SaveUpdate(); })

                    .Override<Tag>(map => { map.References<Importance>(x => x.Importance).Cascade.SaveUpdate(); })
                    .Override<Tag>(map => { map.References<WorkType>(x => x.WorkType).Cascade.SaveUpdate(); })

                    .Override<ProcessTag>(map => { map.References<Process>(x => x.Process).Cascade.SaveUpdate(); })
                    .Override<ProcessTag>(map => { map.References<Tag>(x => x.Tag).Cascade.SaveUpdate(); })

                    .Override<ProcessInstance>(map => { map.References<Process>(x => x.Process).Cascade.SaveUpdate(); })
                    .Override<ProcessInstance>(map => { map.HasMany<TypedWord>(x => x.TypedWords).Cascade.SaveUpdate(); })
                    )
                )

                .ExposeConfiguration((cfg) =>
                {
                    //cfg.Properties.Add("current_session_context_class", "thread_static");
                    new SchemaUpdate(cfg).Execute(false, true);
                });

            this.SessionFactory = fluentConfiguration.BuildSessionFactory();
        }

        public void Stop()
        {
            ISession currentSession = CurrentSessionContext.Unbind(this.SessionFactory);
            if (currentSession != null)
            {
                currentSession.Close();
                currentSession.Dispose();
            }

            this.SessionFactory.Close();
            this.SessionFactory.Dispose();
        }
    }
}
